#include "StdAfx.h"
#include "BigNumber.h"

namespace Crypt {

BigNumber::BigNumber()
{
        _bn = BN_new();
        _array = NULL;
}

BigNumber::BigNumber(const uint8* pBinary, uint32 len)
{
        _bn = BN_new();
        _array = NULL;
        SetBinary(pBinary, len);
}

BigNumber::BigNumber(std::vector<uint8>& vBin)
{
        _bn = BN_new();
        _array = NULL;
        SetBinary(&vBin.front(), vBin.size());
}

BigNumber::BigNumber(std::string hexString)
{
        _bn = BN_new();
        _array = NULL;
        SetHexStr(hexString.c_str());
}

BigNumber::BigNumber(const BigNumber &bn)
{
        _bn = BN_dup(bn._bn);
        _array = NULL;
}

BigNumber::BigNumber(uint32 val)
{
        _bn = BN_new();
        BN_set_word(_bn, val);
        _array = NULL;
}

BigNumber::~BigNumber()
{
        BN_free(_bn);
        delete[] _array;
}

void BigNumber::SetDword(uint32 val)
{
        BN_set_word(_bn, val);
}

void BigNumber::SetQword(uint64 val)
{
        BN_add_word(_bn, (uint32)(val >> 32));
        BN_lshift(_bn, _bn, 32);
        BN_add_word(_bn, (uint32)(val & 0xFFFFFFFF));
}

void BigNumber::SetBinary(const uint8 *bytes, int len)
{
        uint8 t[1000];
        for (int i = 0; i < len && i < 1000; i++) 
                t[i] = bytes[len - 1 - i];
        BN_bin2bn(t, len, _bn);
}

void BigNumber::SetHexStr(const char *str)
{
        BN_hex2bn(&_bn, str);
}

void BigNumber::SetRand(int numbits)
{
        BN_rand(_bn, numbits, 0, 1);
}


BigNumber BigNumber::operator=(const BigNumber &bn)
{
        BN_copy(_bn, bn._bn);
        return *this;
}

BigNumber BigNumber::operator+=(const BigNumber &bn)
{
        BN_add(_bn, _bn, bn._bn);
        return *this;
}

BigNumber BigNumber::operator-=(const BigNumber &bn)
{
        BN_sub(_bn, _bn, bn._bn);
        return *this;
}

BigNumber BigNumber::operator*=(const BigNumber &bn)
{
        BN_CTX *bnctx;

        bnctx = BN_CTX_new();
        BN_mul(_bn, _bn, bn._bn, bnctx);
        BN_CTX_free(bnctx);

        return *this;
}

BigNumber BigNumber::operator/=(const BigNumber &bn)
{
        BN_CTX *bnctx;

        bnctx = BN_CTX_new();
        BN_div(_bn, NULL, _bn, bn._bn, bnctx);
        BN_CTX_free(bnctx);

        return *this;
}

BigNumber BigNumber::operator%=(const BigNumber &bn)
{
        BN_CTX *bnctx;

        bnctx = BN_CTX_new();
        BN_mod(_bn, _bn, bn._bn, bnctx);
        BN_CTX_free(bnctx);

        return *this;
}

BigNumber BigNumber::Exp(const BigNumber &bn)
{
        BigNumber ret;
        BN_CTX *bnctx;

        bnctx = BN_CTX_new();
        BN_exp(ret._bn, _bn, bn._bn, bnctx);
        BN_CTX_free(bnctx);

        return ret;
}

BigNumber BigNumber::ModExp(const BigNumber &bn1, const BigNumber &bn2)
{
        BigNumber ret;
        BN_CTX *bnctx;

        bnctx = BN_CTX_new();
        BN_mod_exp(ret._bn, _bn, bn1._bn, bn2._bn, bnctx);
        BN_CTX_free(bnctx);

        return ret;
}

const int BigNumber::GetNumBytes(void) const
{
        return BN_num_bytes(_bn);
}

const uint32 BigNumber::AsDword() const
{
        return (uint32)BN_get_word(_bn);
}

const uint8* BigNumber::AsByteArray() const
{
        if (_array) {
                delete[] _array;
                _array = NULL;
        }
        _array = new uint8[GetNumBytes()];
        BN_bn2bin(_bn, (unsigned char*)_array);

        std::reverse(_array, _array + GetNumBytes());

        return _array;
}

const std::vector<uint8> BigNumber::AsVector() const
{
        uint32 len = GetNumBytes();
        const uint8* pArr = AsByteArray();
        std::vector<uint8> ret(len, 0);
        ret.assign(pArr, pArr + len);
        return ret;
}

const char* BigNumber::AsHexStr()
{
        return BN_bn2hex(_bn);
}

const char* BigNumber::AsDecStr()
{
        return BN_bn2dec(_bn);
}

};